#include "../mqcommon/mq_msg.pb.h"
#include "../mqcommon/mq_helper.hpp"

namespace zdfmq
{

    /**
     * 路由工具类：负责消息队列中路由键（Routing Key）和绑定键（Binding Key）的合法性校验，
     * 以及不同类型交换机（DIRECT/FANOUT/TOPIC）的路由匹配逻辑
     */
    class Router
    {
    public:
        // ====================== 路由键合法性校验（Routing Key） ======================
        /**
         * 校验路由键是否合法：
         * 合法字符范围：a-z, A-Z, 0-9, ., _
         * @param routing_key 待校验的路由键
         * @return 合法返回 true，否则 false
         */
        static bool isLegalRoutingKey(const std::string &routing_key)
        {
            for (char ch : routing_key)
            { // 遍历路由键每个字符
                // 判断字符是否在合法范围内
                if ((ch >= 'a' && ch <= 'z') ||
                    (ch >= 'A' && ch <= 'Z') ||
                    (ch >= '0' && ch <= '9') ||
                    (ch == '_' || ch == '.'))
                {
                    continue; // 合法字符，继续下一个
                }
                return false; // 发现非法字符，返回 false
            }
            return true; // 全部字符合法，返回 true
        }

        // ====================== 绑定键合法性校验（Binding Key） ======================
        /**
         * 校验绑定键是否合法，包含三部分逻辑：
         * 1. 字符合法性：a-z, A-Z, 0-9, ., _, *, #
         * 2. 通配符（*、#）规则：必须独立成段（如 "news.*.music" 合法，"news.m*sic" 非法）
         * 3. 通配符连续规则：* 和 # 不能连续出现（如 "news.*#"、"news##" 非法）
         * @param binding_key 待校验的绑定键
         * @return 合法返回 true，否则 false
         */
        static bool isLegalBindingKey(const std::string &binding_key)
        {
            // 1. 字符合法性校验
            for (char ch : binding_key)
            {
                if ((ch >= 'a' && ch <= 'z') ||
                    (ch >= 'A' && ch <= 'Z') ||
                    (ch >= '0' && ch <= '9') ||
                    (ch == '_' || ch == '.') ||
                    (ch == '*' || ch == '#'))
                {
                    continue; // 合法字符，继续
                }
                return false; // 非法字符，返回 false
            }

            // 2. 通配符独立成段校验：按 . 分割成单词，检查 *、# 是否独立存在
            std::vector<std::string> sub_words;
            StrHelper::split(binding_key, ".", sub_words); // 按 . 分割绑定键
            for (std::string &word : sub_words)
            {
                // 如果单词长度 >1 且包含 * 或 #，说明通配符未独立成段
                if (word.size() > 1 &&
                    (word.find("*") != std::string::npos ||
                     word.find("#") != std::string::npos))
                {
                    return false;
                }
            }

            // 3. 通配符连续规则校验：遍历分割后的单词，检查相邻通配符是否违规
            for (size_t i = 1; i < sub_words.size(); ++i)
            {
                const std::string &prev = sub_words[i - 1];
                const std::string &curr = sub_words[i];
                // 禁止以下组合：*#、##、#*
                if ((prev == "*" && curr == "#") ||
                    (prev == "#" && curr == "#") ||
                    (prev == "#" && curr == "*"))
                {
                    return false;
                }
            }

            return true; // 所有规则通过，返回 true
        }

        // ====================== 路由匹配逻辑（核心） ======================
        /**
         * 根据交换机类型，执行不同的路由匹配逻辑：
         * - DIRECT：路由键必须与绑定键完全相等
         * - FANOUT：所有绑定队列都接收消息（直接返回 true）
         * - TOPIC：使用动态规划实现通配符匹配（支持 * 匹配一段，# 匹配多段）
         * @param type          交换机类型（DIRECT/FANOUT/TOPIC）
         * @param routing_key   消息的路由键
         * @param binding_key   队列的绑定键
         * @return 匹配成功返回 true，否则 false
         */
        static bool route(ExchangeType type, const std::string &routing_key, const std::string &binding_key)
        {
            // 直接交换（DIRECT）：严格相等匹配
            if (type == ExchangeType::DIRECT)
            {
                return (routing_key == binding_key);
            }
            // 扇形交换（FANOUT）：所有绑定队列都接收，直接返回 true
            else if (type == ExchangeType::FANOUT)
            {
                return true;
            }
            // 主题交换（TOPIC）：通配符模式匹配（核心逻辑）
            else
            {
                // 1. 按 . 分割绑定键和路由键为单词数组
                std::vector<std::string> bkeys, rkeys;
                int n_bkey = StrHelper::split(binding_key, ".", bkeys); // 分割绑定键
                int n_rkey = StrHelper::split(routing_key, ".", rkeys); // 分割路由键

                // 2. 初始化动态规划（DP）表：dp[i][j] 表示绑定键前 i 个单词与路由键前 j 个单词是否匹配
                std::vector<std::vector<bool>> dp(n_bkey + 1, std::vector<bool>(n_rkey + 1, false));
                dp[0][0] = true; // 初始状态：空字符串匹配空字符串

                // 3. 处理绑定键以 # 开头的特殊情况：# 可匹配空路由键
                for (size_t i = 1; i <= bkeys.size(); ++i)
                {
                    if (bkeys[i - 1] == "#")
                    {
                        dp[i][0] = true; // # 可匹配空路由键，所以 dp[i][0] = true
                    }
                    else
                    {
                        break; // 非 # 开头，停止处理
                    }
                }

                // 4. 动态规划填充匹配表
                for (size_t i = 1; i <= n_bkey; ++i)
                {
                    for (size_t j = 1; j <= n_rkey; ++j)
                    {
                        const std::string &bword = bkeys[i - 1]; // 当前绑定键单词
                        const std::string &rword = rkeys[j - 1]; // 当前路由键单词

                        if (bword == rword || bword == "*")
                        {
                            // 情况 1：绑定键单词是 * 或与路由键单词完全相等，继承左上角状态
                            dp[i][j] = dp[i - 1][j - 1];
                        }
                        else if (bword == "#")
                        {
                            // 情况 2：绑定键单词是 #，可匹配任意段，继承 左上、左、上 三个方向的状态
                            dp[i][j] = dp[i - 1][j - 1] || dp[i][j - 1] || dp[i - 1][j];
                        }
                        // 其他情况：不匹配，保持 false（默认初始化已处理）
                    }
                }

                // 5. 最终状态：绑定键所有单词与路由键所有单词是否匹配
                return dp[n_bkey][n_rkey];
            }
        }
    };

} // namespace zdfmq